/**
 * 简单的浅拷贝，Object.assign
 * @returns 
 */
function assign() {
    for (let i = arguments.length - 1; i > 0; i--) {
      for (let key in arguments[i]) {
        if (key === "__proto__") {
          continue;
        }
  
        arguments[i - 1][key] = arguments[i][key];
      }
    }
  
    return arguments[0];
}


/**
 * 防抖
 */
function debounce(fn, wait) {
    let timer

    return () => {
        clearTimeout(timer)
        timer = setTimeout(()=>{
            fn()
        }, wait)
    }
}

/**
 * 节流
 */
function throttle(fn, wait) {
    let lastTime = 0

    return function() {
        let now = new Date().getTime()
        if(now - lastTime > wait){
            fn.call(this, ...arguments)
            lastTime = now
        }
    }

}


/**
 * 自定义bind
 */
 Function.prototype.myBind = function (obj) {
    const fn = this

    return function() {
        // 方法1：
        // fn.call(obj, ...arguments)

        // 方法2：
        // fn.apply(obj, arguments)

        // 方法3：
        obj.fn = fn
        obj.fn(...arguments)
    }
}

function obj (name){
    this.name = name
    this.onClick = function (){
        console.log("click")
        console.log(this.name)
    }

    this.showName = function (name){
        console.log(this.name)
        console.log(name)

    }
}

const oinstence = new obj("name1")
const oname2 = new obj("name2")

// // 测试代码
// const dClick = debounce(oinstence.onClick, 2000)
// const tClick = throttle(oinstence.onClick, 2000).bind(oinstence)
// document.getElementById("button").addEventListener("click", tClick)

// // 测试代码
// const myFunc = oinstence.showName.myBind(oname2)
// myFunc("test")


/**
 * 深拷贝：实现对象的深拷贝，原型链只能浅拷贝和lodash保持一致
 * @param {*} object 
 */
function deepClone(o){
    // console.log("o:", o)
    // console.log(Object.prototype.toString.call(o))
    let chain = []
    return _deep(o)

    function _deep(o){
        // 1、首先判断是否存在循环引用
        if(typeof o === "object" && chain.indexOf(o) > -1){
            console.log("chain:", chain)
            throw Error("存在循环引用！")
            return
        }
        chain.push(o)

        // 2、根据类型做处理、array、object、function、其它
        const type = Object.prototype.toString.call(o)

        if(type === "[object Array]"){
            const array = [] 
            o.forEach(element => {
              array.push(_deep(element))
            });
            return array
        }
    
        if(type === "[object Object]"){
            // 如果不考虑原型就使用
            // let obj = Object.create(null)
            // let obj = {} // o的构造函数是Object时倒是没问题

            // 拷贝的更准确，浅拷贝了原型链：将obj指向o的构造函数
            let obj = new o.constructor()

            // for...in可以循环不可枚举属性 
            for(const key in o){
                const item = o[key]
                if(o.hasOwnProperty(key)){
                    obj[key] = _deep(item)
                }
            }
            return obj
    
            // 如果不考虑原型就使用
            // Object.keys 只能循环对象可枚举属性
            // Object.keys(o).forEach(key => {
            //     console.log("key:", key)
            // })
        }
    
        if(type === "[object Function]"){
            function func (){
                o.call(this, ...arguments)
            }

            // 原型链浅拷贝
            func.prototype = o.prototype
            return func
        }

        return o
    }
}

// 测试代码
function MakeObj () {
    this.number = 10
    this.string = "string"
    this.boolean = true
    this.null = null
    this.array = [1,2,3,4,5],
    this.subObj = {
        subArray1: [1,2,3,4,5],
        subArray2: [
            {name: "zhangsan"},
            {name: "lisi"}
        ]
    }
    this.date = new Date()
    this.reg = /test/gi
    this.func = function() {
        console.log("this:", this)
        console.log("string:", this.string)
    }
}
const obj1 = new MakeObj()

import _ from "lodash"
obj1.__proto__.p1 = "p1 text"
obj1.func.__proto__.p2 = "p2 __proto__"
obj1.func.prototype.p2 = "p2 prototype"
obj1.array.__proto__.p3 = "p3 text"
// obj1.subObj.circle = obj1 // 循环引用

// 测试代码
const obj2 = deepClone(obj1)
const obj3 = _.cloneDeep(obj1)

// 修改obj1数据，查看是否是深拷贝
obj1.subObj.subArray1[1] = 9
obj1.subObj.subArray2[1] = {name: "lisi changed"}
obj1.date = new Date("1990-01-01 00:00:00")
obj1.reg = /asd/
obj1.__proto__.p1 = "p1 changed"
obj1.func.__proto__.p2 = "p2 __proto__ changed"
obj1.func.prototype.p2 = "p2 prototype changed"
obj1.array.__proto__.p3 = "p3 changed"

console.log("obj1:", obj1)
console.log("obj2:", obj2)
console.log("obj3:", obj3)
obj2.func()

export default obj1


// 完整的数据类型
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';